/**
 *  Copyright 2007-2008 University Of Southern California
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */


package edu.isi.pegasus.planner.partitioner;

import edu.isi.pegasus.common.logging.LogManagerFactory;
import edu.isi.pegasus.planner.common.PegasusProperties;
import edu.isi.pegasus.common.logging.LogManager;

import java.util.List;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import java.io.File;
import java.io.IOException;

/**
 * This callback writes out a <code>DAX</code> file for each of the partitions,
 * and also writes out a <code>PDAX</code> file that captures the relations
 * between the partitions.
 *
 * @author not attributable
 * @version $Revision: 2576 $
 */

public class WriterCallback implements Callback {


    /**
     * The handle to the partition graph writer.
     */
    protected PDAXWriter mPDAXWriter;

    /**
     * The handle to the dax writer that writes out the dax corresponding to the
     * partition identified. The base name of the partition is gotten from it.
     */
    protected DAXWriter mDAXWriter;

    /**
     * The path to the PDAX file written out.
     */
    protected String mPDAX;

    /**
     * Handle to the properties available.
     */
    protected PegasusProperties mProps;

    /**
     * The handle to the logger object.
     */
    protected LogManager mLogger;

    /**
     * A boolean indicating that the partitioning has started. This is set,
     * by the first call to the cbPartition( Partition ) callback.
     */
    protected boolean mPartitioningStarted;

    /**
     * The default constructor.
     *
     */
    public WriterCallback(){
        //mLogger = LogManager.getInstance();
    }

    /**
     * Initializes the Writer Callback.
     *
     * @param properties the properties passed to the planner.
     * @param daxFile    the path to the DAX file that is being partitioned.
     * @param daxName    the namelabel of the DAX as set in the root element of the DAX.
     * @param directory  the directory where the partitioned daxes have to reside.
     */
    public void initialize (PegasusProperties properties,
                            String daxFile,
                            String daxName,
                            String directory ){

        mProps  = properties;
        mLogger =  LogManagerFactory.loadSingletonInstance( properties );
        //load the writer for the partitioned daxes
        mDAXWriter = DAXWriter.loadInstance( properties, daxFile, directory );
        mDAXWriter.setPartitionName( daxName );

        //name of pdax file is same as the dax file
        //meaning the name attribute in root element are same.
        mPDAXWriter =  getHandletoPDAXWriter( daxFile, daxName, directory ) ;

        //write out the XML header for the PDAX file
        mPDAXWriter.writeHeader();
    }

    /**
     * Callback for when a partitioner determines that partition has been
     * constructed. A DAX file is written out for the partition.
     *
     * @param p the constructed partition.
     *
     * @throws RuntimeException in case of any error while writing out the DAX or
     *         the PDAX files.
     */
    public void cbPartition( Partition p ) {
        mPartitioningStarted = true;

        //not sure if we still need it
        p.setName( mDAXWriter.getPartitionName() );

        //for time being do a localize catch
        //till i change the interface
        try{
            //write out the partition information to the PDAX file
            mLogger.log( "Writing to the pdax file for partition " + p.getID(),
                         LogManager.DEBUG_MESSAGE_LEVEL);
            mPDAXWriter.write( p );
            mLogger.log( "Writing to the pdax file for partition -DONE" + p.getID(),
                          LogManager.DEBUG_MESSAGE_LEVEL );
            //write out the DAX file
            mDAXWriter.writePartitionDax( p );

        }
        catch( IOException ioe ){
            //wrap and throw in Runtime Exception
            throw new RuntimeException( "Writer Callback for partition " + p.getID(),
                                        ioe );
        }

    }

    /**
     * Callback for when a partitioner determines the relations between partitions
     * that it has previously constructed.
     *
     * @param child    the id of a partition.
     * @param parents  the list of <code>String</code> objects that contain
     *                 the id's of the parents of the partition.
     *
     *
     * @throws RuntimeException in case of any error while writing out the DAX or
     *         the PDAX files.
     */
    public void cbParents( String child, List parents ) {
        mPDAXWriter.write( partitionRelation2XML( child, parents ) );
    }

    /**
     * Callback for the partitioner to signal that it is done with the processing.
     * This internally closes all the handles to the DAX and PDAX writers.
     *
     */
    public void cbDone(){
        //change internal state to signal
        //that we are done with partitioning.
        mPartitioningStarted = false;

        mPDAXWriter.close();
        mDAXWriter.close();
    }


    /**
     * Returns the name of the pdax file written out.
     * Will be null if the partitioning has not completed.
     *
     * @return path to the pdax file.
     */
    public String getPDAX(){
        return this.mPDAX;
    }


    /**
     * Returns the name of the partition, that needs to be set while creating
     * the Partition object corresponding to each partition.
     *
     * @return the name of the partition.
     */
    protected String getPartitionName(){
        return mDAXWriter.getPartitionName();
    }

    /**
     * It returns the handle to the writer for writing out the pdax file
     * that contains the relations amongst the partitions and the jobs making
     * up the partitions.
     *
     * @param daxFile    the path to the DAX file that is being partitioned.
     * @param name  the name/label that is to be assigned to the pdax file.
     * @param directory  the directory where the partitioned daxes have to reside.
     *
     * @return handle to the writer of pdax file.
     */
    protected PDAXWriter getHandletoPDAXWriter( String daxFile,
                                                String name,
                                                String directory ){
        String pdaxPath;
        //get the name of dax file sans the path
        String daxName = new java.io.File( daxFile ).getName();
        //construct the basename of the pdax file
        pdaxPath = (daxName == null)?
                       "partition":
                       ((daxName.indexOf('.') > 0)?
                           daxName.substring(0,daxName.indexOf('.')):
                           daxName)
                         ;
        //now the complete path
        pdaxPath = directory + File.separator + pdaxPath + ".pdax";
        //System.out.println("Name is " + nameOfPDAX);
        mPDAX = pdaxPath;

        return new PDAXWriter( name, pdaxPath );

    }

    /**
     * Returns the xml description of a relation between 2 partitions.
     *
     * @param childID   the ID of the child.
     * @param parentID  the ID of the parent.
     *
     * @return the XML description of child parent relation.
     */
    protected String partitionRelation2XML( String childID , String parentID ){
        StringBuffer sb = new StringBuffer();
        sb.append("\n\t<child ref=\"").append(childID).append("\">");
        sb.append("\n\t\t<parent ref=\"").append(parentID).append("\"/>");
        sb.append("\n\t</child>");
        return sb.toString();
    }

    /**
     * Returns the xml description of a relation between 2 partitions.
     *
     * @param childID   the ID of the child
     * @param parentIDs <code>List</code> of parent IDs.
     *
     * @return the XML description of child parent relations.
     */
    protected String partitionRelation2XML( String childID , List parentIDs ){
        StringBuffer sb = new StringBuffer();
        sb.append("\n\t<child ref=\"").append(childID).append("\">");
        for( Iterator it = parentIDs.iterator(); it.hasNext(); ){
            sb.append("\n\t\t<parent ref=\"").append(it.next()).append("\"/>");
        }
        sb.append("\n\t</child>");
        return sb.toString();
    }

    /**
     * Returns the xml description of a relation between 2 partitions.
     *
     * @param childID   the ID of the child
     * @param parentIDs <code>Set</code> of parent IDs.
     *
     * @return the XML description of child parent relations.
     */
    protected String partitionRelation2XML( String childID , Set parentIDs ){
       StringBuffer sb = new StringBuffer();
       sb.append("\n\t<child ref=\"").append(childID).append("\">");
       for ( Iterator it = parentIDs.iterator(); it.hasNext(); ){
           sb.append("\n\t\t<parent ref=\"").append(it.next()).append("\"/>");
       }
       sb.append("\n\t</child>");
       return sb.toString();
   }


}
